#ifndef _GLFW_APP_H_
#define _GLFW_APP_H_
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <stdio.h>

typedef GLboolean (*app_InitFunc)(GLFWwindow*);
typedef void (*app_RunFunc)(GLFWwindow*, double, int, int);
typedef void (*app_DestroyFunc)();

static struct {
	GLFWwindow *wnd;
	int width, height;
} _glfw_params;

static void 
glfw_onresize(GLFWwindow *wnd, int w, int h) {
	if (w == 0 || h == 0)
		return;
    _glfw_params.width = w;
    _glfw_params.height = h;
    printf("window size change to w:%d h:%d\n", w, h);
} 

static void 
app_destroy(app_DestroyFunc func) {
	if (func)
		func();

	glfwDestroyWindow(_glfw_params.wnd);
	glfwTerminate();
	printf("app destroy now\n");
}

static GLFWwindow*
app_init(int w, int h, const char *title, app_InitFunc func) {
	GLFWwindow *wnd = NULL;

	if (!glfwInit())
		return wnd;

	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	wnd = glfwCreateWindow(w, h, title, NULL, NULL);
	if (wnd == NULL) {
		printf("fail to create window\n");
		return NULL;
	}

	glfwMakeContextCurrent(wnd);
	glfwSetInputMode(wnd, GLFW_STICKY_KEYS, GL_TRUE);

	glfwSetWindowSizeCallback(wnd, glfw_onresize);

	GLenum err = glewInit();
	if (err != GLEW_OK) {
		printf("fail to init glew, err:%s\n", glewGetErrorString(err));
		app_destroy(NULL);
		return GL_FALSE;
	}
	printf("use glew version:%s\n", glewGetString(GLEW_VERSION));

	if (func && !func(wnd)) {
		printf("fail to init user-defined func\n");
		app_destroy(NULL);
		return NULL;
	}

	_glfw_params.wnd = wnd;
	_glfw_params.width = w;
	_glfw_params.height = h;
	printf("app init successfully\n");
	return wnd;
}

static void 
app_run(app_RunFunc func) {
	GLFWwindow *wnd = _glfw_params.wnd;
	if (!wnd)
		return;
	printf("app is running now\n");

	while (!glfwWindowShouldClose(wnd) && glfwGetKey(wnd, GLFW_KEY_ESCAPE) != GLFW_PRESS) {
		glViewport(0, 0, _glfw_params.width, _glfw_params.height);

		if (func)
			func(wnd, glfwGetTime(), _glfw_params.width, _glfw_params.height);

		glfwSwapBuffers(wnd);
		glfwPollEvents();
	}
}

#endif
